----Mr. Robot and His Robot Factory----
A 4am crack                  2017-08-09
---------------------------------------

Name: Mr. Robot and His Robot Factory
Genre: arcade
Year: 1984
Credits: Ron Rosen, Bob McNally
Publisher: Datamost
Platform: Apple ][+ or later
Media: single-sided 5.25-inch floppy
OS: custom

                   ~

               Chapter 0
 In Which Various Automated Tools Fail
          In Interesting Ways


COPYA
  no errors, but copy reboots endlessly

Locksmith Fast Disk Backup
  ditto

EDD 4 bit copy (no sync, no count)
  ditto

Copy ][+ nibble editor
  nothing suspicious

Disk Fixer
  bootloader is custom
  no disk catalog on any track
  no DOS
  everything is custom

Why didn't any of my copies work?
  I'm guessing there's a runtime
  protection check somewhere. Disks do
  not simply reboot unless someone
  tells them to.

Next steps:

  1. Find the check
  2. Disable the check
  3. Declare victory (*)

(*) go to the gym

                   ~

               Chapter 1
         How Do I Reboot Thee?
         Let Me Count The Ways


As with many protection checks, this
disk reboots as soon as it fails. There
are many ways to reboot an Apple II,
but the simplest is a direct jump to
$C600, the slot 6 firmware. (The more
"correct" way is to jump to $FAA6,
which does a "cold" restart and scans
your slots from 7 down to 1 looking for
card firmware to run. Since I have a
hard drive device in slot 7, I can tell
this protection check is not doing
that, because it reboots slot 6 when it
fails.)

Anyway...

[Disk Fixer]
  ["F"ind]
    ["H"ex]
      "4C 00 C6"

Two matches, both in the same sector.

                 --v--

T09,S06
----------- DISASSEMBLY MODE ----------
0000:A2 07          LDX   #$07
0002:A0 00          LDY   #$00
0004:A9 00          LDA   #$00

; check if slot peripheral space is
; writeable, by writing a #$00 to the
; first byte of each slot and checking
; if it worked
0006:8D 00 C7       STA   $C700
0009:AD 00 C7       LDA   $C700
000C:D0 06          BNE   $0014
000E:88             DEY
000F:D0 F3          BNE   $0004

; if that worked, reboot immediately
0011:4C 00 C6       JMP   $C600

; check other all 7 slots
0014:CE 08 19       DEC   $1908
0017:CE 0B 19       DEC   $190B
001A:CA             DEX
001B:D0 E5          BNE   $0002

(Fun fact: this check fails in some
modern emulators, because they emulate
non-existent cards by filling the slot
ROM with #$00 bytes which triggers a
false positive here.)

(Fun fact #2: I don't know what this
check is for. One theory was that it
was protection against hardware NMI
cards like the Wildcard which allowed
users to unconditionally break into the
monitor and save the contents of memory
despite the program's best efforts to
block it. But a friend tested this code
on a real Apple II with a Wildcard Plus
card installed, and it did not reboot.
So either that card specifically works
around this check, or the theory was
incorrect. Mysteries abound, even 30+
years later!)


                   ~

               Chapter 2
        How Do I (M)align Thee?
         Let Me Count The Ways


; Not sure what this is, either. Some
; side effect to be checked later?
; Based on the self-modifying loop
; above, I know this code is loaded at
; $1900, so $1600 is not anywhere near
; the code I'm looking at.
001D:A9 60          LDA   #$60
001F:8D 00 16       STA   $1600
0022:A2 FF          LDX   #$FF
0024:86 04          STX   $04
0026:E8             INX
0027:86 01          STX   $01
0029:86 03          STX   $03

; Looking at it in a sector editor, it
; is pretty clear that there's an RWTS
; parameter table starting at $19EA.
; We're setting up a read (command $01)
; of track $01, sector $00 into $0200.
002B:8E EF 19       STX   $19EF
002E:A2 02          LDX   #$02
0030:8E F3 19       STX   $19F3
0033:A2 01          LDX   #$01
0035:8E EE 19       STX   $19EE
0038:A9 01          LDA   #$01
003A:8D F6 19       STA   $19F6

; Call the RWTS (not shown) to read
; T01,S00.
003D:20 E3 19       JSR   $19E3

; increment the track
0040:EE EE 19       INC   $19EE

; new RWTS command = 0 (seek), not read
0043:A9 00          LDA   #$00
0045:8D F6 19       STA   $19F6

; seek to track 2
0048:20 E3 19       JSR   $19E3

; wait
004B:A9 44          LDA   #$44
004D:20 A8 FC       JSR   $FCA8

; turn on slot 6 drive motor manually
0050:A2 60          LDX   #$60
0052:9D 89 C0       STA   $C089,X

; A is 0 coming out of the WAIT routine
; at $FCA8
0055:85 00          STA   $00

; Death Counter?
0057:E6 00          INC   $00
0059:F0 33          BEQ   $008E

; find the next address prologue
005B:BD 8C C0       LDA   $C08C,X
005E:10 FB          BPL   $005B
0060:C9 D5          CMP   #$D5

; loop back if we didn't find a $D5
; nibble (increments Death Counter, so
; kind of important that we find one
; sooner rather than later)
0062:D0 F3          BNE   $0057
0064:BD 8C C0       LDA   $C08C,X
0067:10 FB          BPL   $0064
0069:C9 AA          CMP   #$AA
006B:D0 F3          BNE   $0060
006D:A0 02          LDY   #$02
006F:BD 8C C0       LDA   $C08C,X
0072:10 FB          BPL   $006F
0074:C9 96          CMP   #$96
0076:D0 E8          BNE   $0060

; get the physical sector number (loop
; burns through and discards the first
; two 4-and-4-encoded values in the
; address field, which are the disk
; volume number and the track number,
; then exits after parsing the sector
; number into zp$02)
0078:BD 8C C0       LDA   $C08C,X
007B:10 FB          BPL   $0078
007D:2A             ROL
007E:85 02          STA   $02
0080:BD 8C C0       LDA   $C08C,X
0083:10 FB          BPL   $0080
0085:25 02          AND   $02
0087:88             DEY
0088:10 EE          BPL   $0078

; is it sector 7?
008A:C9 07          CMP   #$07

; yes -> branch forward
008C:F0 04          BEQ   $0092

; no -> set a flag
008E:A2 FF          LDX   #$FF
0090:86 01          STX   $01

; Execution continues here regardless
; of which sector we found. Keep exact
; track of how long to took to find the
; next address prologue. Maximum number
; of nibbles ends up in zp$03; minimum
; number in zp$04.
0092:A5 00          LDA   $00
0094:C5 03          CMP   $03
0096:90 02          BCC   $009A
0098:85 03          STA   $03
009A:C5 04          CMP   $04
009C:B0 02          BCS   $00A0
009E:85 04          STA   $04

; Loop back and do this all again for
; tracks $02-$10.
00A0:AD EE 19       LDA   $19EE
00A3:C9 11          CMP   #$11
00A5:D0 91          BNE   $0038

; Calculate the difference between the
; minimum and maximum number of nibbles
; before each track's next address
; prologue.
00A7:A5 03          LDA   $03
00A9:38             SEC
00AA:E5 04          SBC   $04
00AC:C9 20          CMP   #$20

; If the difference is less than #$20,
; branch forward.
00AE:90 04          BCC   $00B4

; Otherwise, set a different flag.
00B0:A2 FE          LDX   #$FE
00B2:86 01          STX   $01

; Execution continues here regardless
; of the nibble count difference.
; Turn off the slot 6 drive motor.
00B4:A2 60          LDX   #$60
00B6:9D 88 C0       STA   $C088,X

; Check that flag.
00B9:A6 01          LDX   $01

; If negative, some error occurred.
; Either we found the wrong sector on
; one of the tracks, or the difference
; in nibbles-before-address-prologue
; between tracks was too large.
00BB:10 03          BPL   $00C0

; Either way, just reboot.
00BD:4C 00 C6       JMP   $C600

; Execution continues here only if the
; protection check passes. Decrypt the
; entire game in place ($6000..$BFFF).
00C0:A0 FF          LDY   #$FF
00C2:B9 FF BE       LDA   $BEFF,Y
00C5:59 00 BF       EOR   $BF00,Y
00C8:99 00 BF       STA   $BF00,Y
00CB:88             DEY
00CC:C0 FF          CPY   #$FF
00CE:D0 F2          BNE   $00C2
00D0:CE C4 19       DEC   $19C4
00D3:CE C7 19       DEC   $19C7
00D6:CE CA 19       DEC   $19CA
00D9:AD C7 19       LDA   $19C7
00DC:C9 5F          CMP   #$5F
00DE:D0 E2          BNE   $00C2

; Jump to game start.
00E0:4C 00 76       JMP   $7600

                 --^--

                   ~

               Chapter 3
         How Do I Patch Thee?
        Let Me Count The Bytes


Whew, that's quite a strict protection!
It require that tracks $01-$10 be
physically aligned, relative to each
other, so that "blindly" seeking to the
next track always finds sector $07. But
that's not all! It literally counts the
number of nibbles it takes to find the
start of sector $07 (after a "blind"
seek onto each track) and demands that
it not vary too much between tracks --
no more than $20 nibbles difference
between the minimum and maximum across
15 tracks!

This would be very difficult to
duplicate with a bit copier, even with
the "synchronize tracks" option. In
fact, I suspect this protection scheme
was specifically designed to test the
limits of that option. As there is no
sync sensor on standard Apple II floppy
drives, the "synchronize tracks" option
had to rely on seeking back to track 0,
finding a known pattern (like the start
of T00,S00), then forward to the next
track to write. This got less and less
precise as the track number increased,
because it spent more time seeking the
drive head between tracks, with only
approximate control over how far the
disk would spin before arriving at the
destination track.

The entire design of DOS (and ProDOS,
Pascal, and everything) compensates for
the fact that you never quite know
where you're going to end up on a track
when you get there. This copy protection
weaponizes that imprecision.

Anyway, it's a one-byte patch to change
the "JMP $C600" (which reboots) to "BIT
$C600" (which does nothing). After all
that work, it'll just fall through to
the success path after failing and
start the game anyway.

T09,S06,$BD: 4C -> 2C

Oh, and one more byte so it doesn't
reboot if it finds a #$00 in the first
byte of a slot ROM. I would welcome any
feedback on what that was supposed to
protect against.

T09,S06,$11: 4C -> 2C

Quod erat liberandum.

---------------------------------------
A 4am crack                    No. 1357
------------------EOF------------------
